package com.itcast.springboot3x.thread;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * https://www.cnblogs.com/mic112/p/15424574.html
 * 扩展ThreadPoolExecutor的实现类
 * @author zhangtian105
 * @date 2023/5/17
 * @apiNote
 */
public class ThreadPoolExecutorForMonitor extends ThreadPoolExecutor {
    /**
     * 默认线程池名
     */
    private static final String defaultPoolName="Default-Task";
    /**
     * 默认拒绝策略: 直接抛出java.util.concurrent.RejectedExecutionException异常
     */
    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
    /**
     * 默认创建线程工厂
     */
    private static ThreadFactory threadFactory = new MonitorThreadFactory(defaultPoolName);

    /**
     * 构造器创建线程池
     * @param corePoolSize
     * @param maximumPoolSize
     * @param keepAliveTime
     * @param unit
     * @param workQueue
     */
    public ThreadPoolExecutorForMonitor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                        BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
    }

    /**
     * 构造器创建线程池
     * @param corePoolSize
     * @param maximumPoolSize
     * @param keepAliveTime
     * @param unit
     * @param workQueue
     * @param poolName
     */
    public ThreadPoolExecutorForMonitor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                        BlockingQueue<Runnable> workQueue, String poolName) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new MonitorThreadFactory(poolName), defaultHandler);
    }

    /**
     * 构造器创建线程池
     * @param corePoolSize
     * @param maximumPoolSize
     * @param keepAliveTime
     * @param unit
     * @param workQueue
     * @param poolName
     * @param handler
     */
    public ThreadPoolExecutorForMonitor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                        BlockingQueue<Runnable> workQueue, String poolName, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new MonitorThreadFactory(poolName), handler);
    }

    /**
     * 构造器创建线程池
     * @param corePoolSize
     * @param maximumPoolSize
     * @param keepAliveTime
     * @param unit
     * @param workQueue
     * @param handler
     * @param poolName
     */
    public ThreadPoolExecutorForMonitor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                        BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler, String poolName) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new MonitorThreadFactory(poolName), handler);
    }

    /**
     * 构造器创建线程池
     * @param corePoolSize
     * @param maximumPoolSize
     * @param keepAliveTime
     * @param unit
     * @param workQueue
     * @param threadFactory
     * @param handler
     * @param poolName
     */
    public ThreadPoolExecutorForMonitor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                        BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler, String poolName) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    // 最短执行时间 这里初始是0
    private long minCostTime ;
    // 最长执行时间 这里初始是0
    private long maxCostTime ;
    // 总的耗时
    private AtomicLong totalCostTime = new AtomicLong() ;
    // 本地线程用于存储计算时差
    private ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>() ;

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        startTimeThreadLocal.set(System.currentTimeMillis()) ;
        super.beforeExecute(t, r) ;
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        try {
            // 执行任务消耗时间
            long costTime = System.currentTimeMillis() - startTimeThreadLocal.get() ;
            // 最大时间默认是0，执行任务中如果发现执行任务时间costTime比maxCostTime大，则最大时间赋值新值costTime，其他维持不变
            maxCostTime = maxCostTime > costTime ? maxCostTime : costTime ;

            // 当初始时，即没有已完成的任务时，最小消耗时间minCostTime初始赋值为costTime
            if(getCompletedTaskCount() == 0){
                minCostTime = costTime;
            }

            // 最小时间默认是0，执行任务中如果发现执行任务时间costTime比minCostTime小，则最小时间赋值新值costTime，其他维持不变
            minCostTime = minCostTime < costTime ? minCostTime : costTime;
            // 记录总消耗时间 累加
            totalCostTime.addAndGet(costTime);
            super.afterExecute(r, t) ;
        }finally {
            startTimeThreadLocal.remove();
        }
    }

    public long getMinCostTime() {
        return minCostTime ;
    }

    public long getMaxCostTime() {
        return maxCostTime;
    }

    /**
     * 平均耗时计算，拿总消耗时间除以完成任务数即是平均消耗时间
     * @return
     */
    public long getAverageCostTime(){
        // 当没有任务完成或任务花费总时间为0时，则平均时间为0
        if(getCompletedTaskCount() == 0 || totalCostTime.get() == 0){
            return 0;
        }
        return totalCostTime.get()/getCompletedTaskCount();
    }

    @Override
    public void shutdown() {
        super.shutdown();
    }

    @Override
    protected void terminated() {
        super.terminated();
    }

    static class MonitorThreadFactory implements ThreadFactory {
        /**
         * 线程池编号
         */
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        /**
         * 线程组
         */
        private final ThreadGroup group;
        /**
         * 线程编号
         */
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        /**
         * 名称前缀
         */
        private final String namePrefix;

        MonitorThreadFactory(String poolName) {
            group = Thread.currentThread().getThreadGroup();
            namePrefix = poolName + "-pool-" + poolNumber.getAndIncrement() + "-thread-";
        }

        public Thread newThread(Runnable r) {
            /**
             * Thread的stackSize与-Xss参数的区别
             * https://blog.csdn.net/l569590478/article/details/127569315
             */
            Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);

            // 如果是守护线程，设置为非守护
            if (t.isDaemon()) {
                t.setDaemon(false);
            }

            // 如果不是常态化线程（如优先级过低或过高），设置为常态化线程
            if (t.getPriority() != Thread.NORM_PRIORITY) {
                t.setPriority(Thread.NORM_PRIORITY);
            }

            return t;
        }
    }
}
